package com.niiwoo.civet.trade.service.local.plan;

import com.niiwoo.civet.trade.constant.AmountHandleConstant;
import com.niiwoo.civet.trade.dto.request.loan.RepaymentPlanDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.MathContext;
import java.util.*;

/**
 * 等额本息加息
 *
 * @author liaojunjun
 *
 */
@Service
public class AverageCapitalPlusInterestAddRate {

	private final static Logger logger = LoggerFactory.getLogger(AverageCapitalPlusInterestAddRate.class);

	public BigDecimal getTotalAddInterest(BigDecimal amount, BigDecimal yearRate, Integer deadline, Integer addRateTotalPeriod) {
		logger.info("getTotalAddInterest amount:{},yearRate:{},deadline:{},addRateTotalPeriod:{}",amount, yearRate, deadline,addRateTotalPeriod);
		List<RepaymentPlanDTO> repaymentPlanDTOList = this.getRepaymentPlan(amount, yearRate, deadline);
		BigDecimal result = BigDecimal.ZERO;
		for (RepaymentPlanDTO repaymentPlanDTO : repaymentPlanDTOList){
			if(repaymentPlanDTO.getCurrentPeriod() <= addRateTotalPeriod) {
				result = result.add(repaymentPlanDTO.getInterest());
			}
		}
		result = result.setScale(2, BigDecimal.ROUND_HALF_EVEN);
		logger.info("getTotalAddInterest result:{}",result);
		return result;
	}

	public BigDecimal getAddInterest(BigDecimal amount, BigDecimal yearRate, Integer deadline, Integer addRateTotalPeriod, Integer currentPeriod) {
		logger.info("getAddInterest amount:{},yearRate:{},deadline:{},addRateTotalPeriod:{},currentPeriod:{}",amount, yearRate, deadline,addRateTotalPeriod, currentPeriod);
		BigDecimal result = BigDecimal.ZERO;
		BigDecimal totalInterest = getTotalAddInterest(amount, yearRate, deadline,addRateTotalPeriod);
		List<RepaymentPlanDTO> repaymentPlanDTOList = this.getRepaymentPlan(amount, yearRate, deadline);
		for (RepaymentPlanDTO repaymentPlanDTO : repaymentPlanDTOList){
			if(currentPeriod < addRateTotalPeriod && currentPeriod == repaymentPlanDTO.getCurrentPeriod()){
				result = repaymentPlanDTO.getInterest();
				break;
			} else if(currentPeriod == addRateTotalPeriod && currentPeriod == repaymentPlanDTO.getCurrentPeriod()){
				BigDecimal preInterest = BigDecimal.ZERO;
				for (RepaymentPlanDTO temp : repaymentPlanDTOList){
					if(temp.getCurrentPeriod() < currentPeriod) {
						preInterest = preInterest.add(temp.getInterest().setScale(2, BigDecimal.ROUND_HALF_EVEN));
					}
				}
				result = totalInterest.subtract(preInterest);
				break;
			}
		}
		result = result.setScale(2, BigDecimal.ROUND_HALF_EVEN);
		logger.info("getAddInterest result:{}",result);
		return result;
	}

	public static void main(String[] args) {
		AverageCapitalPlusInterestAddRate averageCapitalPlusInterestAddRate = new AverageCapitalPlusInterestAddRate();
		System.out.println(averageCapitalPlusInterestAddRate.getTotalAddInterest(BigDecimal.valueOf(1300), BigDecimal.valueOf(0.01), 6, 3));
		System.out.println(averageCapitalPlusInterestAddRate.getAddInterest(BigDecimal.valueOf(1300), BigDecimal.valueOf(0.01), 6, 3, 1));
		System.out.println(averageCapitalPlusInterestAddRate.getAddInterest(BigDecimal.valueOf(1300), BigDecimal.valueOf(0.01), 6, 3, 2));
		System.out.println(averageCapitalPlusInterestAddRate.getAddInterest(BigDecimal.valueOf(1300), BigDecimal.valueOf(0.01), 6, 3, 3));
		System.out.println(averageCapitalPlusInterestAddRate.getAddInterest(BigDecimal.valueOf(1300), BigDecimal.valueOf(0.01), 6, 3, 4));
		System.out.println(averageCapitalPlusInterestAddRate.getAddInterest(BigDecimal.valueOf(1300), BigDecimal.valueOf(0.01), 6, 3, 5));
		System.out.println(averageCapitalPlusInterestAddRate.getAddInterest(BigDecimal.valueOf(1300), BigDecimal.valueOf(0.01), 6, 3, 6));
		System.out.println(averageCapitalPlusInterestAddRate.getAddInterest(BigDecimal.valueOf(1300), BigDecimal.valueOf(0.01), 6, 3, 7));
	}

	private List<RepaymentPlanDTO> getRepaymentPlan(BigDecimal amount, BigDecimal yearRate, Integer deadline){
		List<RepaymentPlanDTO> result = new ArrayList<>();
		// 月利率： 年化利率/12
		BigDecimal monthRate = yearRate.divide(BigDecimal.valueOf(AmountHandleConstant.YEAR_OF_MONTH),
				MathContext.DECIMAL128);

		Map<Integer, BigDecimal> capitalMap = getPerMonthCapital(amount, monthRate, deadline);
		Map<Integer, BigDecimal> interestMap = getPerMonthInterest(amount, monthRate, deadline);

		BigDecimal capitalTotal = BigDecimal.ZERO;


		for (int i = 1; i < deadline + 1; i++) {
			RepaymentPlanDTO repaymentPlanDTO = new RepaymentPlanDTO();
			repaymentPlanDTO.setCurrentPeriod(i);
			repaymentPlanDTO.setPrincipal(capitalMap.get(i));
			repaymentPlanDTO.setInterest(interestMap.get(i));

			capitalTotal = capitalTotal.add(repaymentPlanDTO.getPrincipal());

			// 应还款本金不等于实际还款金额时 最后一期做本金抹平处理
			if (i == deadline) {
				repaymentPlanDTO.setPrincipalInterest(getLastPerMonthPrincipalInterest(amount, monthRate, deadline));
				if (amount.compareTo(capitalTotal) != 0) {
					repaymentPlanDTO.setPrincipal(capitalMap.get(i).add(amount.subtract(capitalTotal)));
					repaymentPlanDTO.setInterest(interestMap.get(i).subtract(amount.subtract(capitalTotal)));
				}
				if(repaymentPlanDTO.getInterest().compareTo(BigDecimal.ZERO) < 0){
					repaymentPlanDTO.setInterest(BigDecimal.ZERO);
					repaymentPlanDTO.setPrincipalInterest(repaymentPlanDTO.getPrincipal());
				}
			}

			result.add(repaymentPlanDTO);
		}
		return result;
	}




	/**
	 * 等额本息计算每月偿还本金和利息
	 * <p>
	 * 公式：每月偿还本息=〔贷款本金×月利率×(1＋月利率)＾还款月数〕÷〔(1＋月利率)＾还款月数-1〕
	 *
	 * @param capital
	 *            本金
	 * @param monthRate
	 *            月利息
	 * @param totalPeriod
	 *            总期数
	 * @return
	 */
	private BigDecimal getPerMonthPrincipalInterest(BigDecimal capital, BigDecimal monthRate, int totalPeriod) {
		BigDecimal dividend = capital.multiply(monthRate.multiply(BigDecimal.ONE.add(monthRate).pow(totalPeriod)));
		BigDecimal divisor = (BigDecimal.ONE.add(monthRate)).pow(totalPeriod).subtract(BigDecimal.ONE);
		BigDecimal result = dividend.divide(divisor, AmountHandleConstant.CALCULATE_PRECISION_TWO,
				BigDecimal.ROUND_HALF_EVEN);
		return result;
	}
	
	/**
	 * 计算最后一期偿还本金和利息([不做保留两位的每月还款 乘以 总期数] 减去 [保留两位的每月还款 乘以 (总期数 -1)]) 最后保留两位
	 * @param capital
	 * @param monthRate
	 * @param totalPeriod
	 * @return
	 */
	private BigDecimal getLastPerMonthPrincipalInterest(BigDecimal capital, BigDecimal monthRate, int totalPeriod) {
		BigDecimal dividend = capital.multiply(monthRate.multiply(BigDecimal.ONE.add(monthRate).pow(totalPeriod)));
		BigDecimal divisor = (BigDecimal.ONE.add(monthRate)).pow(totalPeriod).subtract(BigDecimal.ONE);
		BigDecimal result = dividend.divide(divisor, MathContext.DECIMAL128);
		
		BigDecimal total = getPerMonthPrincipalInterest(capital, monthRate, totalPeriod).multiply(new BigDecimal(totalPeriod - 1)).setScale(AmountHandleConstant.CALCULATE_PRECISION_TWO,
				BigDecimal.ROUND_HALF_EVEN);
		result = result.multiply(new BigDecimal(totalPeriod)).subtract(total).setScale(AmountHandleConstant.CALCULATE_PRECISION_TWO,
				BigDecimal.ROUND_HALF_EVEN);
		return result;
	}

	/**
	 * 等额本息计每月偿还利息
	 * <p>
	 * 公式：每月偿还利息=贷款本金×月利率×〔(1+月利率)^还款期数-(1+月利率)^(还款月序号-1)〕÷〔(1+月利率)^还款期数-1〕
	 *
	 * @param capital
	 *            总借款额（贷款本金）
	 * @param monthRate
	 *            月利率
	 * @param totalPeriod
	 *            总期数
	 * @return 每月偿还利息
	 */
	private Map<Integer, BigDecimal> getPerMonthInterest(BigDecimal capital, BigDecimal monthRate, int totalPeriod) {
		Map<Integer, BigDecimal> map = new HashMap<Integer, BigDecimal>();
		BigDecimal monthInterest;
		for (int i = 1; i < totalPeriod + 1; i++) {
			BigDecimal multiply = capital.multiply(monthRate);
			BigDecimal sub = (BigDecimal.ONE.add(monthRate).pow(totalPeriod)).subtract(BigDecimal.ONE.add(monthRate)
					.pow(i - 1));
			monthInterest = multiply.multiply(sub).divide(
					(BigDecimal.ONE.add(monthRate).pow(totalPeriod).subtract(BigDecimal.ONE)), MathContext.DECIMAL128);
			monthInterest = monthInterest.setScale(7,
					BigDecimal.ROUND_HALF_EVEN);
			map.put(i, monthInterest);
		}
		return map;
	}

	/**
	 * 等额本息计算每月偿还本金
	 *
	 * @param capital
	 *            总借款额（贷款本金）
	 * @param monthRate
	 *            月利率
	 * @param totalPeriod
	 *            还款期数
	 * @return 每月偿还本金
	 */
	private Map<Integer, BigDecimal> getPerMonthCapital(BigDecimal capital, BigDecimal monthRate, int totalPeriod) {
		BigDecimal monthIncome = getPerMonthPrincipalInterest(capital, monthRate, totalPeriod);
		Map<Integer, BigDecimal> mapInterest = getPerMonthInterest(capital, monthRate, totalPeriod);

		Map<Integer, BigDecimal> mapPrincipal = new HashMap<Integer, BigDecimal>();
		for (Map.Entry<Integer, BigDecimal> entry : mapInterest.entrySet()) {
			if(entry.getKey() < mapInterest.entrySet().size()){
				mapPrincipal.put(entry.getKey(), monthIncome.subtract(entry.getValue()));
			} else {
				mapPrincipal.put(entry.getKey(),
						getLastPerMonthPrincipalInterest(capital, monthRate, totalPeriod).subtract(entry.getValue()));
			}
		}
		return mapPrincipal;
	}
	
}
